/////////////////////////////////////////////////////////////////////////////
//
// (c) 2007 BinaryComponents Ltd.  All Rights Reserved.
//
// http://www.binarycomponents.com/
//
/////////////////////////////////////////////////////////////////////////////

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Windows.Forms;
using SuperListEx.Helper;
using SuperListEx.Utility.Assemblies;
using SuperListEx.Utility.Collections;

namespace SuperListEx.Sections
{
  public class HeaderColumnSectionContainer : SectionContainer
  {
    #region vars
    private InsertArrowResources _insertionArrows = new InsertArrowResources();
    private bool _showingDropPoint = false;
    private readonly EventingList<Column> _columns;
    #endregion

    #region properties
    public bool ShowingMouseDropPoint
    {
      get
      {
        return _showingDropPoint;
      }
      set
      {
        if (_showingDropPoint != value)
        {
          if (_showingDropPoint)
          {
            _insertionArrows.UpArrowWindow.Visible = false;
            _insertionArrows.DownArrowWindow.Visible = false;
          }
          _showingDropPoint = value;
        }
      }
    }

    public EventingList<Column> Columns
    {
      get
      {
        return _columns;
      }
    }

    public virtual bool AllowColumnsToBeDroppedInVoid
    {
      get
      {
        return true;
      }
    }

    /// <summary>
    /// Called by the list to reserve space for the grouping rows.
    /// </summary>
    public ILayoutController LayoutController
    {
      get;
      set;
    }
    #endregion

    #region constructors
    public HeaderColumnSectionContainer(ISectionHost hostControl, EventingList<Column> columns)
      : base(hostControl)
    {
      _columns = columns;
    }
    #endregion

    #region public functions
    public override void Dispose()
    {
      base.Dispose();
      if (_insertionArrows != null)
      {
        _insertionArrows.Dispose();
        _insertionArrows = null;
      }
    }

    public virtual SortOrder GetColumnSortOrder(Column column)
    {
      return column.SortOrder;
    }

    public virtual void SetColumnSortOrder(Column column, SortOrder sortOrder)
    {
      column.SortOrder = sortOrder;
    }
    #endregion

    #region private functions
    private static HeaderColumnSection GetHeaderColumnSectionFromDragData(IDataObject dataObject)
    {
      string[] formats = dataObject.GetFormats();

      foreach (string format in formats)
      {
        List<object> items = dataObject.GetData(format) as List<object>;
        if (items.Count == 1)
        {
          HeaderColumnSection headerColumnSection = items[0] as HeaderColumnSection;

          if (headerColumnSection != null)
          {
            return headerColumnSection;
          }
        }
      }
      return null;
    }

    private void PositionDropWindows(Point pt)
    {
      //
      // Find the before and after sections from the point given
      HeaderColumnSection before;
      HeaderColumnSection after;

      GetDropBoundsSections(pt, out before, out after);

      //
      // Calculate the x position of the insertion arrows
      int xPos;

      if (before != null && after != null)
      {
        xPos = before.HostBasedRectangle.Right + (after.HostBasedRectangle.Left - before.HostBasedRectangle.Right) / 2;
      }
      else if (before == null)
      {
        xPos = HostBasedRectangle.Left;
        if (after != null)
        {
          xPos += (after.HostBasedRectangle.Left - HostBasedRectangle.Left) / 2;
        }
      }
      else
      {
        xPos = before.HostBasedRectangle.Right;
      }

      //
      // Calculate the y position of the insertion arrows
      int top;
      int bottom;

      if (after != null)
      {
        top = after.Rectangle.Top;
        bottom = after.Rectangle.Bottom;
      }
      else if (before != null)
      {
        top = before.Rectangle.Top;
        bottom = before.Rectangle.Bottom;
      }
      else
      {
        top = Rectangle.Top;
        bottom = Rectangle.Bottom;
      }

      //
      // Now we have all the info actually position them.
      ImageWindow downArrowWindow = _insertionArrows.DownArrowWindow;
      ImageWindow upArrowWindow = _insertionArrows.UpArrowWindow;
      Point downArrowPoint = new Point(xPos - downArrowWindow.Width / 2, top - downArrowWindow.Height);

      if (LayoutController != null)
      {
        if (xPos < 0)
        {
          LayoutController.CurrentHorizontalScrollPosition = xPos + GetAbsoluteScrollCoordinates().X;
        }
      }

      downArrowWindow.Location = Host.PointToScreen(downArrowPoint);
      Point upArrowPoint = new Point(xPos - upArrowWindow.Width / 2, bottom);
      upArrowWindow.Location = Host.PointToScreen(upArrowPoint);

      //
      // Finally make them 
      if (!upArrowWindow.Visible)
      {
        upArrowWindow.Visible = true;
      }
      if (!downArrowWindow.Visible)
      {
        downArrowWindow.Visible = true;
      }
    }
    #endregion

    #region public functions
    public override void Drop(DragEventArgs eventArgs, Point dropPoint)
    {
      HeaderColumnSection headerColumnSection = GetHeaderColumnSectionFromDragData(eventArgs.Data);
      HeaderColumnSection before;
      HeaderColumnSection after;

      GetDropBoundsSections(ToWorldPoint(dropPoint), out before, out after);

      int insertionIndex = 0;

      if (headerColumnSection.Parent != this || headerColumnSection.Parent.Columns.IndexOf(headerColumnSection.Column) != insertionIndex - 1)
      {
        if (headerColumnSection.Parent.ShouldRemoveColumnOnDrop(headerColumnSection.Column))
        {
          headerColumnSection.Parent.Columns.Remove(headerColumnSection.Column);
        }
        if (before != null)
        {
          insertionIndex = Columns.IndexOf(before.Column) + 1;
        }

        int columnIndexFound = Columns.IndexOf(headerColumnSection.Column);

        if (columnIndexFound != -1)
        {
          Columns.RemoveAt(columnIndexFound);
          if (insertionIndex > columnIndexFound)
          {
            insertionIndex--;
          }
        }
        Columns.Insert(insertionIndex, headerColumnSection.Column);
      }
    }

    public virtual bool ShouldRemoveColumnOnDrop(Column column)
    {
      return column.MoveBehaviour == MoveToGroupBehaviour.Move;
    }

    public override void DraggingOver(Point pt, IDataObject objectToDrop)
    {
      if (CanDrop(objectToDrop, CanDropQueryContext.Ancestors) && GetHeaderColumnSectionFromDragData(objectToDrop) != null)
      {
        PositionDropWindows(ToWorldPoint(pt));
      }
    }

    public override void DragLeave()
    {
      ShowingMouseDropPoint = false;
    }

    public override bool CanDrop(IDataObject objectToDrop, CanDropQueryContext context)
    {
      bool canDrop = GetHeaderColumnSectionFromDragData(objectToDrop) != null;

      if (canDrop)
      {
        ShowingMouseDropPoint = true;
      }
      return canDrop;
    }
    #endregion

    #region protected functions
    protected virtual HeaderColumnSection CreateHeaderColumnSection(DisplayMode displayModeToCreate, Column column)
    {
      return Host.SectionFactory.CreateHeaderColumnSection(Host, displayModeToCreate, column);
    }

    protected void SyncSectionsToColumns(DisplayMode displayModeToCreate)
    {
      List<HeaderColumnSection> newHeaderList = new List<HeaderColumnSection>();
      Dictionary<Column, HeaderColumnSection> columnToHeaderColumnSection = new Dictionary<Column, HeaderColumnSection>();

      foreach (Section s in Children)
      {
        HeaderColumnSection headerColumnSection = s as HeaderColumnSection;

        if (headerColumnSection != null)
        {
          columnToHeaderColumnSection[headerColumnSection.Column] = headerColumnSection;
        }
      }

      //
      // Add  additions
      foreach (Column column in _columns)
      {
        HeaderColumnSection headerColumnSection;

        if (!columnToHeaderColumnSection.TryGetValue(column, out headerColumnSection))
        {
          headerColumnSection = CreateHeaderColumnSection(displayModeToCreate, column);
        }
        newHeaderList.Add(headerColumnSection);
      }

      //
      // Remove any ones that doesn't exists any more
      foreach (Section s in Children)
      {
        HeaderColumnSection hcs = s as HeaderColumnSection;

        if (hcs != null)
        {
          if (newHeaderList.IndexOf(hcs) == -1)
          {
            hcs.Dispose();
          }
        }
      }

      Children.Clear();
      Children.AddRange(newHeaderList.ToArray());
    }

    protected void GetDropBoundsSections(Point pt, out HeaderColumnSection before, out HeaderColumnSection after)
    {
      before = null;
      after = null;

      foreach (HeaderColumnSection s in Children)
      {
        if (pt.X > s.Rectangle.Right && (before == null || s.Rectangle.Right > before.Rectangle.Right))
        {
          before = s;
        }
        if (pt.X < s.Rectangle.Right && (after == null || s.Rectangle.Right < after.Rectangle.Right))
        {
          after = s;
        }
      }
    }
    #endregion

    #region InsertArrowResources
    /// <summary>
    /// Class that shares out the insertion window resources
    /// </summary>
    private class InsertArrowResources : IDisposable
    {
      public InsertArrowResources()
      {
        _referencesCount++;
      }

      public void Dispose()
      {
        if (!_disposed)
        {
          if (--_referencesCount == 0)
          {
            if (_upArrowWindow != null)
            {
              _upArrowWindow.Close();
              _upArrowWindow = null;
            }
            if (_downArrowWindow != null)
            {
              _downArrowWindow.Close();
              _downArrowWindow = null;
            }
          }
          Debug.Assert(_referencesCount >= 0);
          _disposed = true;
        }
      }

      public ImageWindow UpArrowWindow
      {
        get
        {
          if (_upArrowWindow == null)
          {
            _upArrowWindow = new ImageWindow(_resources.GetIcon("UpArrow.ico"));
          }
          return _upArrowWindow;
        }
      }

      public ImageWindow DownArrowWindow
      {
        get
        {
          if (_downArrowWindow == null)
          {
            _downArrowWindow = new ImageWindow(_resources.GetIcon("DownArrow.ico"));
          }
          return _downArrowWindow;
        }
      }

      private bool _disposed = false;
      private static int _referencesCount = 0;
      private static ImageWindow _upArrowWindow = null;
      private static ImageWindow _downArrowWindow = null;
      private ManifestResources _resources = new ManifestResources("SuperListEx.Resources");
    }
    #endregion
  }
}